var clientID = Math.floor(Math.random()*9999999999);
var remoteRegisteredListeners = {"lastID" : 0};

var serverPage = 'session_server.sjs';
var cachedMsgs = [];
var bCacheRequestMsgs = false;
/*
//For IE compatibility
var Node = {};
Node.ELEMENT_NODE = 1;
Node.ATTRIBUTE_NODE = 2;
Node.TEXT_NODE = 3;
Node.CDATA_SECTION_NODE = 4;
Node.ENTITY_REFERENCE_NODE = 5;
Node.ENTITY_NODE = 6;
Node.PROCESSING_INSTRUCTION_NODE = 7;
Node.COMMENT_NODE = 8;
Node.DOCUMENT_NODE = 9;
Node.DOCUMENT_TYPE_NODE = 10;
Node.DOCUMENT_FRAGMENT_NODE = 11;
Node.NOTATION_NODE = 12;
*/

function getOuterHTML(elem) {
	if (elem.outerHTML) {
		return elem.outerHTML;
	}
    else {  // Firefox
        var attributes = elem.attributes;
        var attrs = "";
        for (var i = 0; i < attributes.length; i++) {
            attrs += " " + attributes[i].name + "=\"" + attributes[i].value + "\"";
        }
        return "<" + elem.tagName + attrs + ">" + elem.innerHTML + "</" + elem.tagName + ">";
    }
}

function __selectElements(root, path) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteSelectElements(root, path);
	}
			
    if (document.evaluate) {    // Firefox, Opera, Google Chrome and Safari
		var xPathRes = document.evaluate ( path, root, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
			
		var array = new Array(xPathRes.snapshotLength);
		for (i=0; i<xPathRes.snapshotLength; i++) {
			if (xPathRes.snapshotItem(i).nodeType = Node.ELEMENT_NODE) {
				array[i] = xPathRes.snapshotItem(i);
			}
		}
					
		return array;
    }
    else {  // Internet Explorer
        alert("Your browser does not support the evaluate method!");
		//TODO alternative implementation
    }

}

function __selectAttributes(root, path) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteSelectAttributes(root, path);
	}

	var elems = __selectElements(root, path);

	var array = new Array(elems.length);
	
	for (i=0; i<elems.length; i++) {
	
		var map = new Object();
		
		if (elems[i].nodeType = Node.ELEMENT_NODE) {
			for (j=0; j<elems[i].attributes.length; j++) {
				 map[elems[i].attributes[j].name] = elems[i].attributes[j].value;
			}
		}
		
		array[i] = map;
	}
	
	return array;

}

function __selectContents(root, path) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteSelectContents(root, path);
	}

	var elems = __selectElements(root, path);
	
	var array = new Array(elems.length);
	
	for (i=0; i<elems.length; i++) {
	
		if (elems[i].nodeType = Node.ELEMENT_NODE) {
			array[i] = elems[i].innerHTML;
		}
		
	}
	
	return array;
}

function __setAttributes(root, path, attributes) {
	if (typeof attributes != "object") return false;
	
	if (root.nodeName == 'HYPERNODE') {
		return __remoteSetAttributes(root, path, attributes);
	}

	var elems = __selectElements(root, path);
	
	for (i=0; i<elems.length; i++) {
	
		if (elems[i].nodeType = Node.ELEMENT_NODE) {
		
			for (var j in attributes) {
				
				if (event.initMutationEvent) {  // all browsers except IE before version 9
					var eventObject = document.createEvent('MutationEvent');
					var attrChange;
					if (elems[i].hasAttribute(j)) {
						attrChange = MutationEvent.MODIFICATION;
					} else {
						attrChange = MutationEvent.ADDITION;
					}
					eventObject.initMutationEvent ("DOMAttrModified", false, true, attributes[j], elems[i].getAttribute(j), attributes[j], j, attrChange);
					elems[i].dispatchEvent(eventObject);
				} else {  // IE before version 9
					//TODO alternative implementation
				}
				
				elems[i].setAttribute(j, attributes[j]);
			}
			
		}
		
	}
	
	return true;
}
/*
function __setContent(root, path, content) {
	if (typeof content == "string") {
		var newContent = content;
	} else if (content.nodeType == Node.ELEMENT_NODE) {
		var newContent = content.outerHTML;
	} else {
		return false;
	}
	
	if (root.nodeName == 'HYPERNODE') {
		return __remoteSetContent(root, path, newContent);
	}

	var elems = __selectElements(root, path);
	
	for (i=0; i<elems.length; i++) {
		
		if (elems[i].nodeType == Node.ELEMENT_NODE) {
		
			if (event.initMutationEvent) {  // all browsers except IE before version 9
				var eventObject = document.createEvent('MutationEvent');
				eventObject.initMutationEvent ("DOMContentModified", true, true, elems[i], elems[i].innerHTML, newContent, null, MutationEvent.MODIFICATION);
				elems[i].dispatchEvent(eventObject);
			} else {  // IE before version 9
					//TODO alternative implementation
			}
				
			elems[i].innerHTML = newContent;
			
		}
		
	}
	
	return true;
}
*/

function __removeElements(root, path) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteRemoveElements(root, path);
	}
	
	var elems = __selectElements(root, path);
	
	for (i=0; i<elems.length; i++) {
		if (elems[i].nodeType == Node.ELEMENT_NODE) {
			elems[i].parentNode.removeChild(elems[i]);
		}
	}
	
	return true;
}

function __registerEventListener(root, path, eventType, fun) {

	if (root.nodeName == 'HYPERNODE') {
		return __remoteRegisterEventListener(root, path, eventType, fun);
	}
	
	var elems = __selectElements(root, path);
	
	for (i=0; i<elems.length; i++) {
		if (elems[i].parentNode) {
			if (elems[i].addEventListener) {  // all browsers except IE before version 9
				elems[i].addEventListener(eventType, fun);
			} else {  // IE before version 9
				//TODO alternative implementation (if any)
			}
		}
	}
	
	return true;
}

function __unregisterEventListener(root, path, eventType, fun) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteUnregisterEventListener(root, path);
	}
	
	var elems = __selectElements(root, path);
	
	for (i=0; i<elems.length; i++) {
	//TODO check that elems[i] is a node
		if (button.removeEventListener) {  // all browsers except IE before version 9
			elems[i].removeEventListener(eventType, fun);
		} else {  // IE before version 9
			//TODO alternative implementation (if any)
		}
	}
}

function __remoteCheckRevisionChanged(root, revision) {

	return __ajaxRequest({ "op" : "checkRevisionChanged",
						   "root" : root.getAttribute('refname'),
						   "revision" : revision,
						   "scope" : root.getAttribute('refscope')});
						   
}

function __remoteSelectElements(root, path) {

	var elems = __ajaxRequest({ "op" : "selectElements",
							    "root" : root.getAttribute('refname'),
								"path" : path,
								"scope" : root.getAttribute('refscope')});
	
	var elems2=new Array();
	
	for (var e in elems) {
		var strElem = elems[e].html;
		
		var shadowElem = document.createElement("div");
		shadowElem.innerHTML = strElem;
		
		var elem = shadowElem.firstChild;
		
		shadowElem.removeChild(shadowElem.firstChild);
		
		___createRemoteElementStub(root, path, e, elem, elems[e].hyperRevision);
		
		elems2.push(elem);
		var cache = root.firstChild;
		if (!cache) {
			cache = document.createElement("div");
			cache.id = "hypernodecache";
			cache.style.visibility = "hidden";
			root.appendChild(cache);
		}
		cache.appendChild(elem);
		
	}
	
	return elems2;
}

function ___createRemoteElementStub(hyperRoot, hyperPath, hyperIndex, stubRoot, hyperRevisionNumber) {
	var elemPath = hyperPath + "["+hyperIndex+1+"]";
	var actions = [];
	//alert("Stub: creation elemPath="+elemPath);
	stubRoot.addEventListener("DOMNodeInserted", 
		function (event) {
			if (event.eventPhase ==  Event.CAPTURING_PHASE) {
				var content; 
				if (event.target.nodeType == Node.ELEMENT_NODE) {
					content = getOuterHTML(event.target);
				} else {
					content = event.target.nodeValue;
				}
				alert("Stub: DOMNodeInserted - event.target="+content+" nodeType="+event.target.nodeType+"Node.ELEMENT_NODE="+Node.ELEMENT_NODE);
				if (event.target.nextElementSibling) {
					var targetNextNodePath = ___getRelativePath(stubRoot, event.target.nextElementSibling, event.target);
					var actionPath = elemPath + targetNextNodePath; 
					actions.push(
						function () {
							alert("Stub: insertBefore - actionPath="+actionPath+" content="+content);
							__remoteInsertBefore(hyperRoot, actionPath, content);
						}
					);
				} else {
					var relatedNodePath = ___getRelativePath(stubRoot, event.relatedNode);
					var actionPath = elemPath + relatedNodePath; 
					alert("event.relatedNode="+ getOuterHTML(event.relatedNode));
					actions.push(
						function () {
							alert("Stub: appendChild - actionPath="+actionPath+" content="+content);
							__remoteAppendChild(hyperRoot, actionPath, content);
						}
					);		
				}
				
			}
		}
	, true);
	
	stubRoot.addEventListener("DOMCharacterDataModified", 
		function (event) {
			if (event.eventPhase ==  Event.CAPTURING_PHASE) {
				alert("Stub: DOMCharacterDataModified - relatedNode="+getOuterHTML(event.target.parentNode)+" data="+event.target.data);
				var relatedNodePath = ___getRelativePath(stubRoot, event.target.parentNode);
				var actionPath = elemPath + relatedNodePath; 
				var content = event.target.data;
				actions.push(
					function () {
						alert("Stub: setText - actionPath="+actionPath+" content="+content);
						__remoteSetText(hyperRoot, actionPath, content);
					}
				);
			}
		}
	, true);
	
	stubRoot.addEventListener("DOMNodeRemoved", 
		function (event) {
			if (event.eventPhase ==  Event.CAPTURING_PHASE) {
				alert("Stub: DOMNodeRemoved - event.target="+getOuterHTML(event.target));
				//if (event.relatedNode.nodeType != Node.ELEMENT_NODE) return;
				var targetNodePath = ___getRelativePath(stubRoot, event.target);
				var actionPath = elemPath + targetNodePath; 
				actions.push(
					function () {
						alert("Stub: removeElement - actionPath="+actionPath);
						__remoteRemoveElements(hyperRoot, actionPath);
					}
				);
			}
		}
	,true);
	
	stubRoot.addEventListener("DOMAttrModified", 
		function (event) {
			if (event.eventPhase ==  Event.CAPTURING_PHASE) {
				alert("Stub: DOMAttrModified");
				if (event.target.nodeType != Node.ELEMENT_NODE) return;
				var targetNodePath = ___getRelativePath(stubRoot, event.target);
				var actionPath = elemPath + targetNodePath; 
				if (event.attrChange == MutationEvent.ADDITION || event.attrChange == MutationEvent.MODIFICATION) {
					
					var attr = {};
					attr[event.attrName] = event.newValue;
					actions.push(
						function () {
							alert("Stub: setAttributes - actionPath="+actionPath+" attr="+JSON.stringify(attr));
							__remoteSetAttributes(hyperRoot, actionPath, attr);
						}
					);					
				} else if (event.attrChange == MutationEvent.REMOVAL) {
					var attrName = event.attrName;
					actions.push(
						function () {
							alert("Stub: removeAttributes - actionPath="+actionPath);
							__remoteRemoveAttributes(hyperRoot, actionPath, [attrName]);
						}
					);
				}
				
			}
		}
	, true);
	
	stubRoot.commit = function () {
		alert("Stub: commit");
		if (__remoteCheckRevisionChanged(hyperRoot, hyperRevisionNumber)) return false;
		for (var i in actions) {
			bCacheRequestMsgs = (i < (actions.length-1));
			actions[i]();
		}
		actions = [];
		return true;
	}
	
}

function ___getRelativePath(root, element, newElem) {
	var path = "";
	var currentElem = element;
	while (currentElem != root) {
		var prevSibling = currentElem.previousSibling;
		index = 0;
		while (prevSibling) {
			if (prevSibling != newElem) {
				index = index + 1;
			}
			prevSibling = prevSibling.previousSibling;
		}
		path = "/*[" + (index+1) + "]" + path;
		currentElem = currentElem.parentNode;
	}
	return path;
}

function ___getRelativePath1(root, element, newElem) {
	var path = "";
	var currentElem = element;
	while (currentElem != root) {
		var prevSibling = currentElem.previousElementSibling;
		index = 0;
		while (prevSibling) {
			if (prevSibling.tagName == element.tagName && prevSibling != newElem) {
				index = index + 1;
			}
			prevSibling = prevSibling.previousElementSibling;
		}
		path = "/" + currentElem.tagName + "[" + (index+1) + "]" + path;
		currentElem = currentElem.parentNode;
	}
	return path;
}

function __remoteSelectAttributes(root, path) {

	return __ajaxRequest({ "op" : "selectAttributes",
						   "root" : root.getAttribute('refname'),
						   "path" : path,
						   "scope" : root.getAttribute('refscope')});
	
}

function __remoteSelectContents(root, path) {

	return __ajaxRequest({ "op" : "selectContents",
						   "root" : root.getAttribute('refname'),
						   "path" : path,
						   "scope" : root.getAttribute('refscope')});
						 
}

//L.Festa
function __remoteAppendChild(root, path, content) {
	alert("__remoteAppendChild("+root+", "+path+", "+content);
	var newContent;
	if (typeof content == "string") {
		newContent = content;
	} else if (content.nodeType == Node.ELEMENT_NODE) {
		newContent = getOuterHTML(content);
	} else {
		return false;
	}

	return __ajaxRequest({ "op" : "appendChild",
						   "root" : root.getAttribute('refname'),
						   "path" : path,
						   "content" : newContent,
						   "scope" : root.getAttribute('refscope')});
	
}
//L.Festa
function __remoteInsertBefore(root, path, content) {
	alert("__remoteInsertBefore("+root+", "+path+", "+content);
	var newContent;
	if (typeof content == "string") {
		newContent = content;
	} else if (content.nodeType == Node.ELEMENT_NODE) {
		newContent = getOuterHTML(content);
	} else {
		return false;
	}

	return __ajaxRequest({ "op" : "insertBefore",
						   "root" : root.getAttribute('refname'),
						   "path" : path, 
						   "content" : newContent,
						   "scope" : root.getAttribute('refscope')});
	
}



function __remoteSetAttributes(root, path, attributes) {

	if (typeof attributes != "string") 
		attributes = JSON.stringify(attributes);

	return __ajaxRequest({ "op" : "setAttributes",
						   "root" : root.getAttribute('refname'),
						   "path" : path,
						   "attributes" : attributes,
						   "scope" : root.getAttribute('refscope')});
	
}

function __remoteSetText(root, path, text) {

	return __ajaxRequest({ "op" : "setText",
						   "root" : root.getAttribute('refname'),
						   "path" : path,
						   "text" : text,
						   "scope" : root.getAttribute('refscope')});
	
}

function __remoteRemoveElements(root, path) {

	return __ajaxRequest({ "op" : "removeElements",
						   "root" : root.getAttribute('refname'),
						   "path" : path,
						   "scope" : root.getAttribute('refscope')});
	
}

function __remoteRemoveAttributes(root, path, attributes) {

	if (typeof attributes != "string") 
		attributes = JSON.stringify(attributes);

	return __ajaxRequest({ "op" : "removeAttributes",
						   "root" : root.getAttribute('refname'),
						   "path" : path,
						   "attributes" : attributes,
						   "scope" : root.getAttribute('refscope')});
	
}

function __remoteRegisterEventListener(root, path, eventType, fun) {

	remoteRegisteredListeners.lastID++;
	remoteRegisteredListeners[remoteRegisteredListeners.lastID] = {"fun" : fun, "root" : root};

	return __ajaxRequest({ "op" : "registerEventListener",
						   "root" : root.getAttribute('refname'),
						   "path" : path,
						   "eventType" : eventType,
						   "funID" : remoteRegisteredListeners.lastID,
						   "scope" : root.getAttribute('refscope')});
						 
}

function __remoteUnregisterEventListener(root, path, eventType, fun) {

	for (var i in remoteRegisteredListeners) {
	
		if (i == fun) {
		
			remoteRegisteredListeners[i] = null;
			
			return __ajaxRequest({ "op" : "unregisterEventListener",
								   "root" : root.getAttribute('refname'),
								   "path" : path,
								   "eventType" : eventType,
								   "funID" : i,
								   "scope" : root.getAttribute('refscope')});
			
		}
		
	}

	
	
}

function __ajaxRequest(msg) {
	var ret;
	
	msg["clientID"] = clientID;
	
	cachedMsgs.push(msg);
	
	if (!bCacheRequestMsgs) {
		$.ajax(
				  {
					// Se su server remoto "vero" (non localhost) non va, passare --> window.location.host + window.location.pathname
					url: percorsoTagliato( window.location.pathname) + serverPage,
					dataType: 'json',
					//data: msg,
					data: {"msgs" : JSON.stringify(cachedMsgs), "clientID" : clientID},
					async: false,
					success: function(data) {
						ret = data.result;
						for (var i in data.events) {
							data.events[i].arg.relatedRoot = remoteRegisteredListeners[data.events[i].funID].root;
							var shadowElem = document.createElement("div");
							shadowElem.innerHTML = data.events[i].arg.relatedNode;
							data.events[i].arg.relatedNode = shadowElem.firstChild;
							shadowElem = document.createElement("div");
							shadowElem.innerHTML = data.events[i].arg.target;
							data.events[i].arg.target = shadowElem.firstChild;
							remoteRegisteredListeners[data.events[i].funID].fun(data.events[i].arg);
						}
					}
				  }
				)
		cachedMsgs = [];
	}
	return ret;
}
// Funzione che taglia la parte dopo l'ultimo /
function percorsoTagliato(s){
	var lastSlash = 0;
	for (var counter=0; counter< s.length; counter++){
		if (s[counter] == '/') lastslash = counter;
	}
	return s.substring(0, lastslash) + "/";
	
}
